import classNames from 'classnames';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import TreeContext from './Context/TreeContext';
import NodeList from './NodeList';
import type {
  DraggableConfig,
  InternalDataNodeForDataMap,
  InternalFlattenNode,
  Key,
  NodeDragAndRopEventParams,
  OnCheckInfo,
  OnSelectInfo,
  TreeNodeProps,
  YmDataNode,
  YmTreeProps,
} from './interface';
import {
  cleanConductCheck,
  conductExpandParent,
  converTreeDataToMap,
  fillConductCheck,
  flattenTreeDataByExpendKeys,
  getDragChildrenKeys,
} from './utils/treeUtils';

function Tree(props: YmTreeProps) {
  const {
    prefixCls = 'ym-tree',
    rootClassName,
    rootStyle,
    treeNodeClassName,
    treeNodeStyle,
    nodeContentWrapperClassName, //ym-tree-node-content-wrapper
    nodeContentWrapperStyle,
    indentWidth = 24,
    showLine = false,
    treeSelectable = true,
    multiple = false,
    treeShowCheckableBox = false,
    disabled = false, //disabled时，不计入expendKeys,无法onselect,oncheck ,不能控制draggable
    draggable = false,
    cannotDrop,
    motion = {
      motionName: 'node-motion',
      onAppearStart: () => ({ height: 0 }),
      onAppearActive: (node) => ({ height: node.scrollHeight }),
      onLeaveStart: (node) => ({ height: node.offsetHeight }),
      onLeaveActive: () => ({ height: 0 }),
    },
    defaultExpandParent = true, //根据 defaultExpandedKeys/expandedKeys找到他们所有的祖先keys
    autoExpandParent,
    defaultExpandAll = false,
    expandedKeys,
    defaultExpandedKeys,
    checkedKeys: propsCheckedKeys,
    defaultCheckedKeys,
    selectedKeys: propsSelectedKeys,
    defaultSelectedKeys,
    treeData,
    fieldNames,
    icon,
    switcherIcon,
    titleRender,
    onExpand,
    loadData,
    onCheck,
    onSelect,
  } = props;
  const firstMountRef = useRef(true);
  const keyEntitiesRef = useRef<Record<Key, InternalDataNodeForDataMap>>({});
  const [flatteNodes, setFlatteNodes] = useState<InternalFlattenNode[]>([]);

  //expend
  const [mergeExpendKeys, setMergeExpendKeys] = useState<Key[]>([]);

  //checkable
  const [stateHalfCheckedKeys, setStateHalfCheckedKeys] = useState<Key[]>([]);
  const [mergeCheckedKeys, setMergeCheckedKeys] = useState<Key[]>([]);
  const opretionResCheckedKeysRef = useRef<Key[] | undefined>(); //当前node check 后的checkedKeys

  //select
  const [mergeSelectedKeys, setMergeSelectedKeys] = useState<Key[]>(() => {
    if (propsSelectedKeys !== undefined) {
      return propsSelectedKeys;
    }
    return defaultSelectedKeys ?? [];
  });

  // mergeSelectedKeys
  useEffect(() => {
    if (!firstMountRef.current) {
      // 组件初始化加载时不执行
      setMergeSelectedKeys(propsSelectedKeys ?? []);
    }
  }, [propsSelectedKeys]);

  // mergeCheckedKeys 注：不支持操作checkbox的时候改变treeData
  useEffect(() => {
    if (!firstMountRef.current) {
      // 组件初始化加载时不执行
      if (opretionResCheckedKeysRef.current !== propsCheckedKeys) {
        //注：当前node check 后 引起的checkedKey-propsCheckedKeys变化，而不是用户直接改变propsCheckedKeys则不必从新计算
        opretionResCheckedKeysRef.current = propsCheckedKeys;
        const { checkedKeys, halfCheckedKeys } = fillConductCheck(
          propsCheckedKeys ?? [],
          keyEntitiesRef.current,
        );
        // console.log('--非初始化--propsCheckedKeys--checkedKeys--:', propsCheckedKeys, checkedKeys)
        setMergeCheckedKeys(checkedKeys);
        setStateHalfCheckedKeys(halfCheckedKeys);
      }
    }
  }, [propsCheckedKeys]);

  //--dragAndDrop--
  const [dragNode, setDragNode] = useState<YmDataNode>();
  const [dragIndex, setDragIndex] = useState<number>();
  const [hoverKey, setHoverKey] = useState<string>();
  const [loadedKeys, setLoadedKeys] = useState<string[]>([]);
  const [loadingKeys, setLoadingKeys] = useState<string[]>([]);

  const onWindowDragEndCapture = () => {
    setDragNode(undefined);
    setDragIndex(undefined);
    setHoverKey(undefined);
  };

  //mergeExpendKeys、flatteNodes
  useEffect(() => {
    if (!firstMountRef.current) {
      //组件初始化加载时不执行
      const resMap = converTreeDataToMap(treeData, fieldNames); //1.计算 mergeExpendKeys
      const { keyMap } = resMap;
      keyEntitiesRef.current = keyMap;
      //expend
      const currExpendKeys: Key[] = expandedKeys ?? mergeExpendKeys;
      const tempMergeKeys: Key[] = autoExpandParent
        ? conductExpandParent(currExpendKeys, keyMap)
        : currExpendKeys;
      // console.log('--非初始化--keyMap--mergeExpendKeys--:', tempMergeKeys);
      setMergeExpendKeys(tempMergeKeys);
      const flattenRenderNodes = flattenTreeDataByExpendKeys(
        treeData,
        fieldNames,
        tempMergeKeys,
      ); //2.根据mergeExpendKeys获取需要渲染的treeData
      setFlatteNodes(flattenRenderNodes);
    }
  }, [autoExpandParent, expandedKeys, treeData, fieldNames]);

  useEffect(() => {
    let tempMergeKeys: Key[] = [];
    const currExpendKeys: Key[] | undefined =
      defaultExpandedKeys ?? expandedKeys ?? [];
    const resMap = converTreeDataToMap(treeData, fieldNames);
    // console.log('--初始化--1.resMap--:', resMap);
    const { keyMap } = resMap;
    keyEntitiesRef.current = keyMap;

    //expend
    if (defaultExpandAll) {
      tempMergeKeys = Object.keys(keyMap);
    } else if (currExpendKeys) {
      tempMergeKeys =
        autoExpandParent ?? defaultExpandParent
          ? conductExpandParent(currExpendKeys, keyMap)
          : currExpendKeys;
    }
    setMergeExpendKeys(tempMergeKeys);
    const flattenRenderNodes = flattenTreeDataByExpendKeys(
      treeData,
      fieldNames,
      tempMergeKeys,
    );
    // console.log('--初始化--2.flattenRenderNodes--expendKeys--:', flattenRenderNodes, tempMergeKeys);
    setFlatteNodes(flattenRenderNodes);

    //check
    const currCheckedKeys = defaultCheckedKeys ?? propsCheckedKeys ?? [];
    const { checkedKeys, halfCheckedKeys } = fillConductCheck(
      currCheckedKeys,
      keyMap,
    );
    setMergeCheckedKeys(checkedKeys);
    setStateHalfCheckedKeys(halfCheckedKeys);
    // console.log('--初始化--2.1--checkedKeys--halfCheckedKeys--:', checkedKeys, halfCheckedKeys)

    firstMountRef.current = false;
    return () => {
      firstMountRef.current = true;
      window.removeEventListener('dragend', onWindowDragEndCapture);
    };
  }, []);

  //1.draggable 未设置（draggable === undefined）说明所有节点不允许拖动，div.draggable = false,虽然绑定了drag/drop事件当时都不会触发
  // 2.nodeDraggable return false 当前节点不允许拖动,但是div.draggable 一定要设置为true,
  const draggableConfig = useMemo<DraggableConfig | undefined>(() => {
    let draggableConfig;
    if (draggable) {
      if (typeof draggable === 'object') {
        draggableConfig = draggable;
      } else if (typeof draggable === 'function') {
        draggableConfig = {
          nodeDraggable: draggable,
        };
      } else {
        draggableConfig = {};
      }
    }
    return draggableConfig;
  }, [draggable]);

  function nodeExpandCallback(
    currExpendKeys: Key[],
    info: { node: TreeNodeProps; expanded: boolean; nativeEvent: any },
  ) {
    if (!expandedKeys) {
      //非受控
      setMergeExpendKeys(currExpendKeys);
      // console.log('--promise--keyEntitiesRef.current--currExpendKeys--:', keyEntitiesRef.current, currExpendKeys);
      const flattenRenderNodes = flattenTreeDataByExpendKeys(
        treeData,
        fieldNames,
        currExpendKeys,
      );
      setFlatteNodes(flattenRenderNodes);
    }
    onExpand?.(currExpendKeys, info);
  }

  //---------打开，关闭--------
  const onNodeExpand = (
    e: React.MouseEvent<HTMLElement>,
    treeNode: TreeNodeProps,
  ) => {
    // console.log('--Tree--onNodeExpand--treeNode--:', treeNode);
    const { expanded, eventKey } = treeNode;
    let currExpendKeys = mergeExpendKeys;
    if (expanded) {
      //打开-关闭
      const expendIndex = currExpendKeys.indexOf(eventKey);
      if (expendIndex > -1) {
        currExpendKeys.splice(expendIndex, 1);
      }
    } else {
      const expendIndex = currExpendKeys.indexOf(eventKey);
      if (expendIndex < 0) {
        currExpendKeys.push(eventKey);
      }
    }

    const info = {
      node: treeNode,
      expanded: !!expanded,
      nativeEvent: e.nativeEvent,
    };
    //异步加载
    const {
      data: { isLeaf },
    } = treeNode;
    if (
      !expanded &&
      typeof loadData === 'function' &&
      !isLeaf &&
      loadedKeys.indexOf(eventKey) < 0 &&
      loadingKeys.indexOf(eventKey) < 0
    ) {
      loadingKeys.push(eventKey);
      setLoadingKeys([...loadingKeys]);
      const promise = loadData(treeNode.data);
      promise
        .then(() => {
          loadedKeys.push(eventKey);
          setLoadedKeys([...loadedKeys]);
          setTimeout(() => {
            //注：异步加载后显然treeData--useEffect执行--更新keyEntities再更新expendkeys
            nodeExpandCallback(currExpendKeys, info);
          });
        })
        .finally(() => {
          const newLoadingKeys = loadingKeys.filter(
            (itemKey) => itemKey !== eventKey,
          );
          setLoadingKeys([...newLoadingKeys]);
        })
        .catch(() => {
          //catch时 不更新expendKeys和flatteNodes
        });
    } else {
      nodeExpandCallback(currExpendKeys, info);
    }
  };

  //-------------checkbox-------------------
  const onNodeCheck = (
    e: React.MouseEvent<HTMLElement>,
    treeNode: TreeNodeProps,
  ) => {
    const { eventKey, checked, data } = treeNode;
    let keysCheckedObj: { checkedKeys: Key[]; halfCheckedKeys: Key[] };

    const checkInfo: OnCheckInfo = {
      checked: !checked,
      node: data,
      checkedNodes: [],
      nativeEvent: e.nativeEvent,
      halfCheckedKeys: [],
    };
    if (!checked) {
      //从未选中--选中
      mergeCheckedKeys.push(eventKey);
      keysCheckedObj = fillConductCheck(
        mergeCheckedKeys,
        keyEntitiesRef.current,
      );
    } else {
      //从选中--未选中
      const newMergeCheckedKeys = mergeCheckedKeys.filter(
        (mergeKey) => mergeKey !== eventKey,
      );
      const newHalfCheckedKeys = stateHalfCheckedKeys.filter(
        (mergeKey) => mergeKey !== eventKey,
      );
      if (newMergeCheckedKeys.length) {
        keysCheckedObj = cleanConductCheck(
          eventKey,
          newMergeCheckedKeys,
          newHalfCheckedKeys,
          keyEntitiesRef.current,
        );
      } else {
        keysCheckedObj = { checkedKeys: [], halfCheckedKeys: [] };
      }
    }
    const { checkedKeys, halfCheckedKeys } = keysCheckedObj;
    if (!propsCheckedKeys) {
      //非受控
      setMergeCheckedKeys(checkedKeys);
      setStateHalfCheckedKeys(halfCheckedKeys);
    } else {
      opretionResCheckedKeysRef.current = checkedKeys;
    }
    checkedKeys.forEach((checkedKey) => {
      const checkedEntity = keyEntitiesRef.current[checkedKey];
      if (checkedEntity) {
        checkInfo.checkedNodes.push(checkedEntity.node);
      }
    });
    checkInfo.halfCheckedKeys = halfCheckedKeys;

    onCheck?.(checkedKeys, checkInfo);
  };

  //-------------select-------------------
  const onNodeSelect = (
    e: React.MouseEvent<HTMLElement>,
    treeNode: TreeNodeProps,
  ) => {
    const { eventKey, selected, data } = treeNode;
    let currSelectedKeys = mergeSelectedKeys;
    const currKeyExistIndex = currSelectedKeys.indexOf(eventKey);
    if (!selected) {
      //由未选中--选中
      if (currKeyExistIndex < 0) {
        if (multiple) {
          //多选
          currSelectedKeys.push(eventKey);
        } else {
          currSelectedKeys = [eventKey];
        }
      }
    } else {
      //由选中--未选中
      if (currKeyExistIndex > -1) {
        currSelectedKeys.splice(currKeyExistIndex, 1);
      }
    }
    if (!propsSelectedKeys) {
      setMergeSelectedKeys([...currSelectedKeys]);
    }
    const selectInfo: OnSelectInfo = {
      selected: !selected,
      node: data,
      selectedNodes: [],
      nativeEvent: e.nativeEvent,
    };
    currSelectedKeys.forEach((sKey) => {
      const sEntity = keyEntitiesRef.current[sKey];
      if (sEntity) {
        selectInfo.selectedNodes.push(sEntity.node);
      }
    });
    onSelect?.(currSelectedKeys, selectInfo);
  };

  //-------------dragAndDrop-------------------

  const onDragStart = ({ event, node }: NodeDragAndRopEventParams) => {
    if (
      draggableConfig &&
      typeof draggableConfig.nodeDraggable === 'function' &&
      !draggableConfig.nodeDraggable(node)
    ) {
      event.preventDefault(); //不会执行后续drag/drop事件
      return;
    }
    const { key } = node;
    window.addEventListener('dragend', onWindowDragEndCapture, true);
    // 有children展开的收起来
    const expendIndex = mergeExpendKeys.indexOf(key!);
    let flattenRenderNodes = flatteNodes;
    if (
      expendIndex > -1 &&
      keyEntitiesRef.current[key!] &&
      keyEntitiesRef.current[key!].children?.length
    ) {
      mergeExpendKeys.splice(expendIndex, 1);
      flattenRenderNodes = flattenTreeDataByExpendKeys(
        treeData,
        fieldNames,
        mergeExpendKeys,
      );
      if (!expandedKeys) {
        //非受控
        setMergeExpendKeys([...mergeExpendKeys]);
      }
      setFlatteNodes(flattenRenderNodes); //注：这里项快点更新flatteNodes,以便在over中拿到最新值
    }
    const dragIndex = flattenRenderNodes.findIndex(
      (nodeItem) => nodeItem.key === key,
    );
    setDragNode(node);
    setDragIndex(dragIndex);
    props.onDragStart?.({ event, node });
  };

  const onDragEnter = ({ event, node }: NodeDragAndRopEventParams) => {
    if (!dragNode || node.key === dragNode.key) {
      //自己enter自己
      return;
    }
    props.onDragEnter?.({ event, node, dragNode });
  };

  const onDragOver = ({ event, node }: NodeDragAndRopEventParams) => {
    const { key } = node;
    const cannotNodeDrop = cannotDrop?.(node); // canotDrop
    const dragChildKeys = getDragChildrenKeys(
      dragNode?.key!,
      keyEntitiesRef.current,
    );
    if (
      cannotNodeDrop ||
      dragIndex === undefined ||
      node.key === dragNode?.key ||
      dragChildKeys.indexOf(key!) > -1
    ) {
      //禁止拖到自己的子级中
      return;
    }

    const hoverIndex = flatteNodes.findIndex(
      (nodeItem) => nodeItem.key === key,
    );
    if (hoverIndex < 0) {
      throw new Error(`key === ${node.key}的节点不在flatteNodes中`);
    }
    const dragOverDom = event.currentTarget; // 事件绑定对象
    const { clientY } = event;
    const hoverBoundingRect = dragOverDom.getBoundingClientRect();
    const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; //高度/2
    const hoverDiffy = clientY - hoverBoundingRect.top;
    // console.log('--onDragOver--dragIndex--hoverIndex--hoverMiddleY--:', dragIndex, hoverIndex, hoverMiddleY)
    // Dragging down
    if (dragIndex < hoverIndex && hoverDiffy < hoverMiddleY) {
      return;
    }
    // Dragging upwards
    if (dragIndex > hoverIndex && hoverDiffy > hoverMiddleY) {
      return;
    }
    setHoverKey(flatteNodes[hoverIndex].key);
    event.preventDefault();
    props.onDragOver?.({ event, node, dragNode });
  };

  const onDragLeave = ({ event, node }: NodeDragAndRopEventParams) => {
    props.onDragLeave?.({ event, node });
  };

  const onDrop = ({ event, node }: NodeDragAndRopEventParams) => {
    // console.log('---onDrop- dragNode--node--:', dragNode, node);
    const cannotNodeDrop = cannotDrop?.(node); // canotDrop
    if (cannotNodeDrop) {
      throw new Error(`key === ${node.key}的节点不能被放置`);
    }
    const dragChildKeys = getDragChildrenKeys(
      dragNode?.key!,
      keyEntitiesRef.current,
    );
    if (dragChildKeys.indexOf(node.key!) > -1) {
      return;
    }
    props.onDrop?.({ event, node, dragNode });
  };

  const onDragEnd = ({ event, node, dragNode }: NodeDragAndRopEventParams) => {
    window.removeEventListener('dragend', onWindowDragEndCapture);
    props.onDragEnd?.({ event, node, dragNode });
  };

  return (
    <TreeContext.Provider
      value={{
        prefixCls,
        icon,
        switcherIcon,
        draggable: draggableConfig,
        treeSelectable,
        treeShowCheckableBox,
        disabled,
        keyEntities: keyEntitiesRef.current,
        multiple,
        treeNodeClassName,
        treeNodeStyle,
        nodeContentWrapperClassName, //ym-tree-node-content-wrapper
        nodeContentWrapperStyle,
        indentWidth,
        titleRender,
        onNodeExpand,
        onNodeCheck,
        onNodeSelect,
        onDragStart,
        onDragEnter,
        onDragOver,
        onDragLeave,
        onDragEnd,
        onDrop,
        hoverKey,
        loadData,
        showLine,
      }}
    >
      <div
        role="tree"
        className={classNames(prefixCls, rootClassName, {
          [`${prefixCls}-show-line`]: showLine,
        })}
        style={rootStyle}
      >
        <NodeList
          motion={motion}
          data={flatteNodes}
          expandedKeys={mergeExpendKeys}
          loadingKeys={loadingKeys}
          loadedKeys={loadedKeys}
          checkedKeys={mergeCheckedKeys}
          halfCheckedKeys={stateHalfCheckedKeys}
          selectedKeys={mergeSelectedKeys}
        />
      </div>
    </TreeContext.Provider>
  );
}

export default Tree;
